package converter

import (
	"fmt"
	"kwdb-import/pkg/parser"
	"regexp"
	"strconv"
	"strings"
)

// MySQLToKWDBConverter MySQL到KWDB转换器
type MySQLToKWDBConverter struct {
	// 当 INSERT 使用多组 VALUES 时，按该上限拆分为多条语句（>0 生效）
	MaxValuesPerInsert int
}

// NewMySQLToKWDBConverter 创建新的转换器
func NewMySQLToKWDBConverter() *MySQLToKWDBConverter {
	return &MySQLToKWDBConverter{}
}

// SetMaxValuesPerInsert 设置每条 INSERT 允许的最大 VALUES 组数量（>0 时触发拆分）
func (c *MySQLToKWDBConverter) SetMaxValuesPerInsert(max int) {
	c.MaxValuesPerInsert = max
}

// Convert 转换SQL语句
func (c *MySQLToKWDBConverter) Convert(stmt parser.SQLStatement, dataMapping map[string]string) (string, error) {
	switch stmt.Type {
	case parser.CreateTable:
		return c.convertCreateTable(stmt, dataMapping), nil
	case parser.DropTable:
		return c.convertDropTable(stmt), nil
	case parser.Insert:
		return c.convertInsert(stmt), nil
	case parser.CreateIndex:
		return c.convertCreateIndex(stmt), nil
	case parser.CreateView:
		return c.convertCreateView(stmt), nil
	case parser.CreateSequence:
		return c.convertCreateSequence(stmt), nil
	case parser.CreateDatabase:
		return c.convertCreateDatabase(stmt), nil
	case parser.UseDatabase:
		return c.convertUseDatabase(stmt), nil
	case parser.CreateProcedure, parser.CreateFunction, parser.CreateTrigger:
		// 这些语句可能不被KWDB支持：返回空字符串以忽略执行与输出
		return "", nil
	case parser.Delimiter:
		// 跳过DELIMITER语句
		return "", nil
	default:
		// 其他语句：针对MySQL专有的SET/@变量与环境语句做兼容处理
		return c.convertOther(stmt), nil
	}
}

// convertCreateTable 转换CREATE TABLE语句
func (c *MySQLToKWDBConverter) convertCreateTable(stmt parser.SQLStatement, dataMapping map[string]string) string {
	sql := stmt.Content
	// 统一移除MySQL反引号，避免在KWDB/PG中产生语法错误
	sql = c.removeMySQLIdentifierQuotes(sql)

	// 移除MySQL特有的选项
	sql = c.removeMySQLOptions(sql)

	// 将 ENUM 列转换为 VARCHAR(255) 并添加 CHECK 约束
	sql = c.convertEnumColumnsToCheck(sql)

	// 转换数据类型
	sql = c.convertDataTypes(sql, dataMapping)
	// 规范二进制类型长度：BYTEA 不接受长度，移除诸如 BYTEA(255)
	sql = c.normalizeBinaryTypeLengths(sql)
	// 规范字符串类型长度：避免出现 VARCHAR(0)/CHAR(0) 等非法定义
	sql = c.normalizeStringTypeLengths(sql)
	// 去除整数类型的显示宽度 (e.g., INT(11) -> INT, BIGINT(20) -> BIGINT, SMALLINT(1) -> SMALLINT)
	sql = c.stripIntegerDisplayWidth(sql)

	// 处理AUTO_INCREMENT
	sql = c.convertAutoIncrement(sql)

	// 处理字符集和排序规则
	sql = c.removeCharsetAndCollation(sql)

	// 移除列定义中的 ON UPDATE CURRENT_TIMESTAMP（PG/KWDB不支持该列级自动更新时间语法）
	sql = regexp.MustCompile(`(?i)\s+ON\s+UPDATE\s+CURRENT_TIMESTAMP(\s*\(\s*\d+\s*\))?`).ReplaceAllString(sql, "")

	// 提取并移除列/表注释，稍后生成 COMMENT ON 语句
	sql, tableName, tableComment, columnComments := c.extractComments(sql)

	// 清理 CREATE TABLE 中的 USING BTREE/HASH 与内联 KEY 定义
	sql = regexp.MustCompile(`(?i)\s+USING\s+BTREE`).ReplaceAllString(sql, "")
	sql = regexp.MustCompile(`(?i)\s+USING\s+HASH`).ReplaceAllString(sql, "")
	sql = c.transformInlineKeys(sql)

	// 为可能与关键字冲突的列名添加双引号，避免语法解析歧义
	sql = c.quoteReservedIdentifiers(sql)

	// 再次确保移除了任何残留的表注释片段
	sql = c.removeTableComment(sql)

	// 删除由于移除 KEY 导致的多余逗号
	sql = regexp.MustCompile(`,\s*\)`).ReplaceAllString(sql, ")")

	// 确保幂等：添加 IF NOT EXISTS
	sql = c.ensureIfNotExists("TABLE", sql)

	// 若存在注释，生成对应的 COMMENT ON 语句并附加
	var sb strings.Builder
	sb.WriteString(sql)
	// 规范结束分号
	trimmed := strings.TrimSpace(sql)
	if !strings.HasSuffix(trimmed, ";") {
		sb.WriteString(";")
	}
	// 表注释
	if tableName != "" && tableComment != "" {
		sb.WriteString("\nCOMMENT ON TABLE ")
		sb.WriteString(tableName)
		sb.WriteString(" IS '")
		sb.WriteString(tableComment)
		sb.WriteString("';")
	}
	// 列注释
	for col, cmt := range columnComments {
		if cmt == "" {
			continue
		}
		sb.WriteString("\nCOMMENT ON COLUMN ")
		sb.WriteString(tableName)
		sb.WriteString(".")
		sb.WriteString(col)
		sb.WriteString(" IS '")
		sb.WriteString(cmt)
		sb.WriteString("';")
	}

	return sb.String()
}

// normalizeBinaryTypeLengths 规范二进制类型的长度标注，去除 BYTEA(n)
// MySQL 的 BINARY(n)/VARBINARY(n)/BLOB 映射到 PG/KWDB 的 BYTEA，但 BYTEA 不接受长度参数
// 示例：VARBINARY(255) -> BYTEA(255) -> BYTEA
func (c *MySQLToKWDBConverter) normalizeBinaryTypeLengths(sql string) string {
	// 移除 BYTEA(任何内容)，保留为 BYTEA
	re := regexp.MustCompile(`(?i)\bBYTEA\s*\([^)]*\)`)
	sql = re.ReplaceAllString(sql, "BYTEA")
	return sql
}

// convertInsert 转换INSERT语句
func (c *MySQLToKWDBConverter) convertInsert(stmt parser.SQLStatement) string {
	sql := stmt.Content
	// 移除MySQL反引号
	sql = c.removeMySQLIdentifierQuotes(sql)

	// 移除MySQL特有的INSERT选项
	sql = regexp.MustCompile(`(?i)\s+IGNORE\s+`).ReplaceAllString(sql, " ")
	sql = regexp.MustCompile(`(?i)\s+ON\s+DUPLICATE\s+KEY\s+UPDATE.*$`).ReplaceAllString(sql, "")

	// 转换MySQL二进制字面量到Postgres/KWDB bytea
	sql = c.convertBinaryLiterals(sql)

	// 转换0x...十六进制为bytea（非引号字面量）
	re0x := regexp.MustCompile(`(?i)\b0x([0-9A-Fa-f]+)\b`)
	// 使用PG原生bytea十六进制输入形式：E'\\x...'::bytea
	sql = re0x.ReplaceAllString(sql, "E'\\\\x$1'::bytea")

	// 修复MySQL字符串中的反斜杠引号转义：\' -> ''
	// 在PostgreSQL默认的standard_conforming_strings=on设置下，\'不会转义单引号，需改为''
	sql = strings.ReplaceAll(sql, "\\'", "''")

	// 修复JSON字符串中的转义双引号：\" -> "
	// MySQL支持在字符串中使用\"，但PostgreSQL/KWDB需要将其转换
	sql = c.fixJSONEscaping(sql)

	// 若配置了按 VALUES 组拆分，则进行拆分
	if c.MaxValuesPerInsert > 0 {
		sql = c.splitInsertValues(sql, c.MaxValuesPerInsert)
	}

	return sql
}

// splitInsertValues 将 "INSERT ... VALUES (...),(...),..." 按最大 VALUES 组数量拆分为多条语句
// 仅在检测到 VALUES 关键字且存在括号组时生效；非 VALUES/SELECT 形式不做处理。
func (c *MySQLToKWDBConverter) splitInsertValues(sql string, max int) string {
	if max <= 0 {
		return sql
	}

	// 寻找 VALUES 关键字位置
	reValues := regexp.MustCompile(`(?i)\bVALUES\b`)
	loc := reValues.FindStringIndex(sql)
	if loc == nil {
		return sql
	}

	prefix := strings.TrimSpace(sql[:loc[1]]) // 包含 VALUES
	valuesPart := strings.TrimSpace(sql[loc[1]:])
	// 去除末尾分号
	if len(valuesPart) > 0 && valuesPart[len(valuesPart)-1] == ';' {
		valuesPart = strings.TrimSpace(valuesPart[:len(valuesPart)-1])
	}

	groups := parseTopLevelParenGroups(valuesPart)
	if len(groups) == 0 {
		// 未解析到括号组，保留原始语句
		return sql
	}
	if len(groups) <= max {
		// 不需要拆分，规范化输出
		return prefix + " " + strings.Join(groups, ", ") + ";"
	}

	var out strings.Builder
	for i := 0; i < len(groups); i += max {
		end := i + max
		if end > len(groups) {
			end = len(groups)
		}
		out.WriteString(prefix)
		out.WriteString(" ")
		out.WriteString(strings.Join(groups[i:end], ", "))
		out.WriteString(";")
		if end < len(groups) {
			out.WriteString("\n")
		}
	}
	return out.String()
}

// parseTopLevelParenGroups 解析形如 "(....),(....),..." 的顶层括号分组，忽略字符串内部的括号与逗号
func parseTopLevelParenGroups(s string) []string {
	var groups []string
	depth := 0
	start := -1
	inString := false
	i := 0
	for i < len(s) {
		ch := s[i]
		if ch == '\'' { // 单引号字符串边界，处理 SQL 中的 '' 转义
			if inString {
				// 遇到成对的 '' 视为字符串内部的引号
				if i+1 < len(s) && s[i+1] == '\'' {
					i += 2
					continue
				}
				inString = false
				i++
				continue
			} else {
				inString = true
				i++
				continue
			}
		}
		if inString {
			i++
			continue
		}
		if ch == '(' {
			if depth == 0 {
				start = i
			}
			depth++
			i++
			continue
		}
		if ch == ')' {
			if depth > 0 {
				depth--
				if depth == 0 && start >= 0 {
					grp := strings.TrimSpace(s[start : i+1])
					if grp != "" {
						groups = append(groups, grp)
					}
					start = -1
				}
			}
			i++
			continue
		}
		// 顶层逗号在此用于分隔，但我们通过括号闭合来截取分组
		i++
	}
	return groups
}

// fixJSONEscaping 修复JSON字符串中的转义问题
// MySQL允许使用\"来转义双引号,但在PostgreSQL/KWDB中需要转换
func (c *MySQLToKWDBConverter) fixJSONEscaping(sql string) string {
	var result strings.Builder
	result.Grow(len(sql))

	inString := false
	i := 0

	for i < len(sql) {
		ch := sql[i]

		// 检测字符串边界(单引号)
		if ch == '\'' {
			result.WriteByte(ch)
			inString = !inString
			i++
			continue
		}

		// 如果在字符串内,处理转义序列
		if inString && ch == '\\' && i+1 < len(sql) {
			next := sql[i+1]

			// 处理 \" -> "
			if next == '"' {
				result.WriteByte('"')
				i += 2
				continue
			}

			// 处理 \\ -> 保持原样(实际上需要在PostgreSQL中可能需要调整)
			// 对于JSON中的\\,保持原样
			result.WriteByte(ch)
			i++
			continue
		}

		result.WriteByte(ch)
		i++
	}

	return result.String()
}

// convertCreateIndex 转换CREATE INDEX语句
func (c *MySQLToKWDBConverter) convertCreateIndex(stmt parser.SQLStatement) string {
	sql := stmt.Content
	// 移除MySQL反引号
	sql = c.removeMySQLIdentifierQuotes(sql)

	// 移除MySQL特有的索引选项
	sql = regexp.MustCompile(`(?i)\s+USING\s+BTREE`).ReplaceAllString(sql, "")
	sql = regexp.MustCompile(`(?i)\s+USING\s+HASH`).ReplaceAllString(sql, "")

	// 添加 IF NOT EXISTS（放在 INDEX 之后），避免丢失 INDEX 关键字
	// 若已存在则不重复添加
	if !regexp.MustCompile(`(?i)^CREATE\s+(UNIQUE\s+)?INDEX\s+IF\s+NOT\s+EXISTS\b`).MatchString(sql) {
		re := regexp.MustCompile(`(?i)^CREATE\s+(UNIQUE\s+)?INDEX\s+`)
		sql = re.ReplaceAllStringFunc(sql, func(m string) string {
			upper := strings.ToUpper(m)
			if strings.Contains(upper, "UNIQUE") {
				return "CREATE UNIQUE INDEX IF NOT EXISTS "
			}
			return "CREATE INDEX IF NOT EXISTS "
		})
	}

	return sql
}

// convertCreateView 转换CREATE VIEW语句
func (c *MySQLToKWDBConverter) convertCreateView(stmt parser.SQLStatement) string {
	sql := stmt.Content
	// 移除MySQL反引号
	sql = c.removeMySQLIdentifierQuotes(sql)

	// 移除MySQL特有的VIEW选项
	sql = regexp.MustCompile(`(?i)\s+ALGORITHM\s*=\s*\w+`).ReplaceAllString(sql, "")
	sql = regexp.MustCompile(`(?i)\s+DEFINER\s*=\s*[^\s]+`).ReplaceAllString(sql, "")
	sql = regexp.MustCompile(`(?i)\s+SQL\s+SECURITY\s+\w+`).ReplaceAllString(sql, "")

	// KWDB 报错 SQLSTATE 0A000: 未实现 CREATE OR REPLACE VIEW，改为纯 CREATE VIEW
	// 如果需要保留替换语义，建议外层执行前先 DROP VIEW IF EXISTS，再 CREATE VIEW
	sql = regexp.MustCompile(`(?i)^\s*CREATE\s+OR\s+REPLACE\s+VIEW\s+`).ReplaceAllString(sql, "CREATE VIEW ")

	// 可选：移除 MySQL 的 WITH CHECK OPTION（PG/KWDB 上通常不需要或语义不同）
	sql = regexp.MustCompile(`(?i)\s+WITH\s+CHECK\s+OPTION\b`).ReplaceAllString(sql, "")

	// 将 MySQL 的 CONCAT(...) 转换为 PG/KWDB 的字符串拼接操作符，并为非字符串参数添加 ::text
	sql = c.convertConcatFunctions(sql)

	// 转换 MySQL 的 CAST ... AS SIGNED/UNSIGNED 为 PG/KWDB 兼容的整数类型
	sql = c.convertCastSignedUnsigned(sql)

	// 处理 MySQL 的 CONVERT(expr USING charset)：在PG/KWDB中通常不需要，直接展开为原始表达式
	sql = c.convertConvertUsingCharset(sql)

	// 将 MySQL 的 isnull(expr) 转换为 PG/KWDB 的 (expr IS NULL)
	sql = c.convertIsNullFunction(sql)

	// 去除不被目标库支持的 schema 限定（如 public.），将 schema.table.column 简化为 table.column
	sql = c.stripSchemaQualification(sql)

	// 规范 AS 子句：移除外层括号 AS (SELECT ...) -> AS SELECT ...
	// 某些 PG 变体/兼容库对 AS (...) 存在解析问题，统一去掉最外层括号以提升兼容性。
	sql = c.stripViewAsOuterParens(sql)

	// 修复 MySQL 风格的隐式连接：FROM (A JOIN B) 且无 ON/USING 条件
	// 在 KWDB/PG 中 JOIN 需要显式 ON/USING；此处改写为逗号连接 FROM A, B，条件放在 WHERE。
	sql = c.rewriteJoinWithoutOn(sql)

	// 视图在多数PG兼容目标上不支持 IF NOT EXISTS，保持最小兼容形式
	// 不添加 IF NOT EXISTS，直接返回标准 CREATE VIEW 语句

	return sql
}

// stripViewAsOuterParens 移除 CREATE VIEW ... AS (...) 外层括号，仅当括号直接包裹整个查询时。
// 示例：
//
//	CREATE VIEW v AS (SELECT * FROM t) ;      -> CREATE VIEW v AS SELECT * FROM t ;
//	CREATE VIEW v AS ((SELECT * FROM t));     -> CREATE VIEW v AS SELECT * FROM t;
//
// 该函数不会触及查询内部的子查询或表达式括号。
func (c *MySQLToKWDBConverter) stripViewAsOuterParens(sql string) string {
	// 寻找 CREATE [OR REPLACE] VIEW ... AS 的位置
	reCreate := regexp.MustCompile(`(?i)\bCREATE\s+(?:OR\s+REPLACE\s+)?VIEW\b`)
	loc := reCreate.FindStringIndex(sql)
	if loc == nil {
		return sql
	}

	// 从视图声明之后开始扫描，定位第一个 AS 关键字（视图的 AS 子句）
	start := loc[1]
	inSingle, inDouble := false, false
	asStart := -1
	for i := start; i < len(sql); i++ {
		ch := sql[i]
		// 引号状态切换，处理 '' 转义
		if ch == '\'' && !inDouble {
			if inSingle {
				if i+1 < len(sql) && sql[i+1] == '\'' {
					i++
				} else {
					inSingle = false
				}
			} else {
				inSingle = true
			}
			continue
		}
		if ch == '"' && !inSingle {
			inDouble = !inDouble
			continue
		}

		if inSingle || inDouble {
			continue
		}

		// 检测单词边界上的 AS
		if (ch == 'A' || ch == 'a') && i+1 < len(sql) && (sql[i+1] == 'S' || sql[i+1] == 's') {
			// 前后需是单词边界
			prev := byte(' ')
			if i-1 >= start {
				prev = sql[i-1]
			}
			next := byte(' ')
			if i+2 < len(sql) {
				next = sql[i+2]
			}
			isPrevBoundary := !(('0' <= prev && prev <= '9') || ('A' <= prev && prev <= 'Z') || ('a' <= prev && prev <= 'z') || prev == '_')
			isNextBoundary := !(('0' <= next && next <= '9') || ('A' <= next && next <= 'Z') || ('a' <= next && next <= 'z') || next == '_')
			if isPrevBoundary && isNextBoundary {
				asStart = i
				break
			}
		}
	}

	if asStart < 0 {
		return sql
	}

	// 跳过 AS 与随后的空白，检查是否为 '('
	j := asStart + 2
	for j < len(sql) && (sql[j] == ' ' || sql[j] == '\t' || sql[j] == '\n' || sql[j] == '\r') {
		j++
	}
	// 可能存在多层冗余括号：AS ((SELECT ...))，循环剥离
	for j < len(sql) && sql[j] == '(' {
		// 找到与该 '(' 对应的 ')'，同时避免进入字符串字面量
		open := j
		depth := 1
		k := j + 1
		inS, inD := false, false
		for k < len(sql) {
			ck := sql[k]
			if ck == '\'' && !inD {
				if inS {
					if k+1 < len(sql) && sql[k+1] == '\'' { // 处理 ''
						k += 2
						continue
					}
					inS = false
				} else {
					inS = true
				}
				k++
				continue
			}
			if ck == '"' && !inS {
				inD = !inD
				k++
				continue
			}
			if inS || inD {
				k++
				continue
			}
			if ck == '(' {
				depth++
				k++
				continue
			}
			if ck == ')' {
				depth--
				k++
				if depth == 0 {
					break
				}
				continue
			}
			k++
		}
		if depth != 0 {
			// 括号不匹配，保持原样
			return sql
		}

		// 确认括号内部以 SELECT 开始（忽略前导空白与括号层）
		inner := sql[open+1 : k-1]
		// 跳过前导空白
		ii := 0
		for ii < len(inner) && (inner[ii] == ' ' || inner[ii] == '\t' || inner[ii] == '\n' || inner[ii] == '\r') {
			ii++
		}
		// 若仍以 '(' 开始，说明是双层括号，允许继续剥离
		if ii < len(inner) && inner[ii] == '(' {
			// 直接用下一轮循环处理
			// 将 sql 改写为移除这一层括号后的内容，以便继续剥离
			sql = sql[:open] + inner + sql[k:]
			// 重新计算 j：仍指向可能的新 '(' 位置
			// j 保持为 open（此处的 open 位置现在是原 inner 的起始）
			j = open
			continue
		}
		// 判断是否以 SELECT 开始
		if ii+6 <= len(inner) && strings.EqualFold(inner[ii:ii+6], "select") {
			// 去掉这一层外括号
			sql = sql[:open] + inner + sql[k:]
			// 更新 j 到 open 位置，以检查是否还有冗余的外层括号
			j = open
			continue
		}
		// 若不是 SELECT，说明这层括号不应剥离
		break
	}

	return sql
}

// rewriteJoinWithoutOn 将 "FROM (table1 alias1 JOIN table2 alias2)" 重写为
// "FROM table1 alias1, table2 alias2"，用于兼容 MySQL 在 WHERE 中声明连接条件但 JOIN 缺少 ON 的写法。
// 仅处理最常见的括号包裹的二元 JOIN 形式。
func (c *MySQLToKWDBConverter) rewriteJoinWithoutOn(sql string) string {
	re := regexp.MustCompile(`(?i)\bFROM\s*\(\s*((?:"[^"]+"|\w+)(?:\s+(?:"[^"]+"|\w+))?)\s+JOIN\s+((?:"[^"]+"|\w+)(?:\s+(?:"[^"]+"|\w+))?)\s*\)`)
	return re.ReplaceAllString(sql, "FROM $1, $2")
}

// convertCastSignedUnsigned 将 MySQL 的 "CAST(expr AS SIGNED[ INTEGER])" 或
// "CAST(expr AS UNSIGNED[ INTEGER])" 转换为 PostgreSQL/KWDB 兼容的整数类型。
// - 统一转换为 INTEGER，满足大多数场景；如需更宽范围可改为 BIGINT。
func (c *MySQLToKWDBConverter) convertCastSignedUnsigned(sql string) string {
	// AS SIGNED [INTEGER] -> AS INTEGER
	reSigned := regexp.MustCompile(`(?i)\bAS\s+SIGNED(\s+INTEGER)?\b`)
	sql = reSigned.ReplaceAllString(sql, "AS INTEGER")

	// AS UNSIGNED [INTEGER] -> AS INTEGER（若需更大范围，可改为 BIGINT）
	reUnsigned := regexp.MustCompile(`(?i)\bAS\s+UNSIGNED(\s+INTEGER)?\b`)
	sql = reUnsigned.ReplaceAllString(sql, "AS INTEGER")

	// 可选：CONVERT(expr, SIGNED/UNSIGNED) -> CAST(expr AS INTEGER)
	reConvertSigned := regexp.MustCompile(`(?i)\bCONVERT\s*\(\s*([^,]+)\s*,\s*SIGNED(\s+INTEGER)?\s*\)`)
	sql = reConvertSigned.ReplaceAllString(sql, "CAST($1 AS INTEGER)")
	reConvertUnsigned := regexp.MustCompile(`(?i)\bCONVERT\s*\(\s*([^,]+)\s*,\s*UNSIGNED(\s+INTEGER)?\s*\)`)
	sql = reConvertUnsigned.ReplaceAllString(sql, "CAST($1 AS INTEGER)")

	return sql
}

// convertConvertUsingCharset 处理 MySQL 的 CONVERT(expr USING charset)
// 在 PG/KWDB 中通常不需要进行字符集转换，直接展开为原始表达式以提升兼容性。
func (c *MySQLToKWDBConverter) convertConvertUsingCharset(sql string) string {
	// 匹配形如 CONVERT( <expr> USING <charset> )，忽略大小写与空白
	// 捕获组1为原始表达式 <expr>
	re := regexp.MustCompile(`(?i)\bCONVERT\s*\(\s*(.*?)\s+USING\s+[\w\d_]+\s*\)`)
	return re.ReplaceAllString(sql, "$1")
}

// convertIsNullFunction 将 MySQL 的 isnull(expr) 转换为 (expr IS NULL)
// 该函数按最小规则处理，不进入字符串字面量；适用于常见的 CASE WHEN isnull(x) 场景。
func (c *MySQLToKWDBConverter) convertIsNullFunction(sql string) string {
	re := regexp.MustCompile(`(?i)\bisnull\s*\((.*?)\)`) // 捕获最短的表达式
	return re.ReplaceAllString(sql, "($1 IS NULL)")
}

// stripSchemaQualification 移除标识符上的schema前缀，兼容不支持schema限定的目标库。
// 规则：
// - CREATE [OR REPLACE] VIEW schema.view -> CREATE [OR REPLACE] VIEW view
// - FROM/JOIN/UPDATE/INTO/DELETE FROM 中的 schema.table -> table
// - 任意位置的 schema.table.column -> table.column
// 注意：不处理字符串字面量中的匹配，简单正则可能误伤复杂场景，但对常见DDL/DML足够。
func (c *MySQLToKWDBConverter) stripSchemaQualification(sql string) string {
	// 1) 处理视图名上的 schema 前缀
	reViewName := regexp.MustCompile(`(?i)\b(CREATE\s+(?:OR\s+REPLACE\s+)?VIEW)\s+((?:"[^"]+"|\w+)\.)((?:"[^"]+"|\w+))`)
	sql = reViewName.ReplaceAllString(sql, `$1 $3`)

	// 2) 处理三段式 schema.table.column -> table.column
	reTriple := regexp.MustCompile(`(?i)\b((?:"[^"]+"|\w+))\.((?:"[^"]+"|\w+))\.((?:"[^"]+"|\w+))`)
	sql = reTriple.ReplaceAllString(sql, `$2.$3`)

	// 3) 处理在 FROM/JOIN/UPDATE/INTO/DELETE FROM 上的 schema.table -> table
	kw := []string{"FROM", "JOIN", "UPDATE", "INTO", "DELETE\\s+FROM"}
	for _, k := range kw {
		pattern := `(?i)\b` + k + `\s+((?:"[^"]+"|\w+))\.((?:"[^"]+"|\w+))`
		re := regexp.MustCompile(pattern)
		sql = re.ReplaceAllString(sql, k+" $2")
	}

	// 3.1) 兜底：直接移除常见schema前缀 public.（避免复杂上下文遗漏）
	sql = regexp.MustCompile(`(?i)\bpublic\.`).ReplaceAllString(sql, "")

	return sql
}

// convertConcatFunctions 将 MySQL 的 CONCAT(a, b, c, ...) 转换为 a || b || c 形式
// 并为非字符串参数添加 ::text 以避免目标数据库对数字/日期等类型的拼接报错。
func (c *MySQLToKWDBConverter) convertConcatFunctions(sql string) string {
	var out strings.Builder
	out.Grow(len(sql))

	inSingle := false // '...'
	inDouble := false // "..."

	lower := strings.ToLower(sql)
	i := 0
	for i < len(sql) {
		ch := sql[i]
		// 处理引号状态切换（含 '' 转义）
		if ch == '\'' && !inDouble {
			out.WriteByte(ch)
			if inSingle {
				if i+1 < len(sql) && sql[i+1] == '\'' {
					// 写入转义的第二个单引号
					i += 2
					out.WriteByte('\'')
					continue
				}
				inSingle = false
			} else {
				inSingle = true
			}
			i++
			continue
		}
		if ch == '"' && !inSingle {
			out.WriteByte(ch)
			inDouble = !inDouble
			i++
			continue
		}

		// 在非字符串上下文中检测 concat(
		if !inSingle && !inDouble && i+7 <= len(sql) && lower[i:i+7] == "concat(" {
			// 解析括号内参数，处理嵌套、引号与逗号分隔
			j := i + 7
			depth := 1
			argStart := j
			args := make([]string, 0, 4)
			innerSingle := false
			innerDouble := false

			for j < len(sql) {
				cj := sql[j]
				if cj == '\'' && !innerDouble {
					// 处理 '' 转义
					if innerSingle {
						if j+1 < len(sql) && sql[j+1] == '\'' {
							j += 2
							continue
						}
						innerSingle = false
					} else {
						innerSingle = true
					}
					j++
					continue
				}
				if cj == '"' && !innerSingle {
					innerDouble = !innerDouble
					j++
					continue
				}
				if !innerSingle && !innerDouble {
					if cj == '(' {
						depth++
						j++
						continue
					}
					if cj == ')' {
						depth--
						// 捕获最后一个参数
						if depth == 0 {
							arg := strings.TrimSpace(sql[argStart:j])
							if len(arg) > 0 {
								args = append(args, arg)
							}
							j++ // 跳过右括号
							break
						}
						j++
						continue
					}
					if cj == ',' && depth == 1 {
						arg := strings.TrimSpace(sql[argStart:j])
						args = append(args, arg)
						j++
						argStart = j
						continue
					}
				}
				j++
			}

			// 将参数转换为 || 拼接，非字符串参数强制 ::text
			conv := make([]string, 0, len(args))
			for _, a := range args {
				aa := strings.TrimSpace(a)
				// 如果是字符串字面量，保持原样；否则包一层并转为 text
				if len(aa) > 0 && aa[0] == '\'' {
					conv = append(conv, aa)
				} else {
					// 保持原始表达式优先级
					conv = append(conv, "("+aa+")::text")
				}
			}

			out.WriteString("(")
			out.WriteString(strings.Join(conv, " || "))
			out.WriteString(")")
			i = j
			continue
		}

		out.WriteByte(ch)
		i++
	}

	return out.String()
}

// convertCreateSequence 转换CREATE SEQUENCE语句
func (c *MySQLToKWDBConverter) convertCreateSequence(stmt parser.SQLStatement) string {
	sql := stmt.Content
	// 移除MySQL反引号
	sql = c.removeMySQLIdentifierQuotes(sql)
	// 添加 IF NOT EXISTS
	sql = c.ensureIfNotExists("SEQUENCE", sql)
	return sql
}

// convertCreateDatabase 转换CREATE DATABASE语句
func (c *MySQLToKWDBConverter) convertCreateDatabase(stmt parser.SQLStatement) string {
	sql := stmt.Content
	// 移除MySQL反引号
	sql = c.removeMySQLIdentifierQuotes(sql)

	// 移除字符集和排序规则
	sql = c.removeCharsetAndCollation(sql)

	// 可选：添加 IF NOT EXISTS（虽然执行阶段会跳过）
	sql = c.ensureIfNotExists("DATABASE", sql)

	return sql
}

// convertUseDatabase 转换USE语句
func (c *MySQLToKWDBConverter) convertUseDatabase(stmt parser.SQLStatement) string {
	// 同步去除反引号，尽管执行阶段会跳过
	content := c.removeMySQLIdentifierQuotes(stmt.Content)
	re := regexp.MustCompile(`(?i)^\s*USE\s+([a-zA-Z0-9_]+)\s*;?\s*$`)
	if re.MatchString(content) {
		db := re.FindStringSubmatch(content)[1]
		// 转换为 Postgres/KWDB 风格的数据库切换命令
		return "\\c " + db + ";"
	}
	return content
}

// convertOther 处理非专门分类的语句，移除或兼容MySQL专有语法
func (c *MySQLToKWDBConverter) convertOther(stmt parser.SQLStatement) string {
	sql := strings.TrimSpace(stmt.Content)
	upper := strings.ToUpper(sql)

	// 跳过事务控制语句：BEGIN/START TRANSACTION/COMMIT/ROLLBACK/SAVEPOINT/RELEASE SAVEPOINT
	if regexp.MustCompile(`(?i)^\s*(BEGIN|START\s+TRANSACTION)\b`).MatchString(sql) ||
		regexp.MustCompile(`(?i)^\s*(COMMIT|ROLLBACK)\b`).MatchString(sql) ||
		regexp.MustCompile(`(?i)^\s*(SAVEPOINT|RELEASE\s+SAVEPOINT)\b`).MatchString(sql) {
		return ""
	}
	// 跳过 MySQL 的自动提交设置：SET AUTOCOMMIT = 0/1
	if regexp.MustCompile(`(?i)^\s*SET\s+AUTOCOMMIT\s*=\s*(0|1)\b`).MatchString(sql) {
		return ""
	}

	// 跳过 MySQL 用户/系统变量赋值：SET @var = ...; 或依赖 @@variable
	if regexp.MustCompile(`(?i)^SET\s+@`).MatchString(sql) ||
		regexp.MustCompile(`(?i)^SET\s+\w+\s*=\s*@`).MatchString(sql) ||
		strings.Contains(upper, "@@CHARACTER_SET") ||
		strings.Contains(upper, "@@SQL_MODE") ||
		strings.Contains(upper, "@@TIME_ZONE") ||
		strings.Contains(upper, "@@UNIQUE_CHECKS") ||
		strings.Contains(upper, "@@FOREIGN_KEY_CHECKS") ||
		strings.Contains(upper, "@@SQL_NOTES") {
		return ""
	}

	// 跳过 MySQL 环境相关 SET 语句（目标PG/Cockroach不支持或无意义）
	if regexp.MustCompile(`(?i)^SET\s+NAMES\b`).MatchString(sql) ||
		regexp.MustCompile(`(?i)^SET\s+CHARACTER_SET_(CLIENT|RESULTS)\b`).MatchString(sql) ||
		regexp.MustCompile(`(?i)^SET\s+COLLATION_CONNECTION\b`).MatchString(sql) ||
		regexp.MustCompile(`(?i)^SET\s+SQL_MODE\b`).MatchString(sql) ||
		regexp.MustCompile(`(?i)^SET\s+(FOREIGN_KEY_CHECKS|UNIQUE_CHECKS|SQL_NOTES)\b`).MatchString(sql) ||
		regexp.MustCompile(`(?i)^SET\s+TIME_ZONE\b`).MatchString(sql) {
		return ""
	}

	// 跳过 MySQL 锁表语句
	if regexp.MustCompile(`(?i)^LOCK\s+TABLES\b`).MatchString(sql) ||
		regexp.MustCompile(`(?i)^UNLOCK\s+TABLES\b`).MatchString(sql) {
		return ""
	}

	// 其他语句：最小化处理以提高兼容性
	return c.removeMySQLIdentifierQuotes(sql)
}

// normalizeStringTypeLengths 规范字符串类型长度，修正非法或边界长度
// - VARCHAR(0) -> VARCHAR(1)
// - CHAR(0)    -> CHAR(1)
// 未来如需更严格策略，可改为映射为 TEXT
func (c *MySQLToKWDBConverter) normalizeStringTypeLengths(sql string) string {
	// 修正显式为0的情况
	reVarcharZero := regexp.MustCompile(`(?i)\bVARCHAR\s*\(\s*0\s*\)`)
	sql = reVarcharZero.ReplaceAllString(sql, "VARCHAR(1)")

	reCharZero := regexp.MustCompile(`(?i)\bCHAR\s*\(\s*0\s*\)`)
	sql = reCharZero.ReplaceAllString(sql, "CHAR(1)")

	return sql
}

// convertDataTypes 转换数据类型
func (c *MySQLToKWDBConverter) convertDataTypes(sql string, dataMapping map[string]string) string {

	// 数据类型映射
	typeMap := make(map[string]string)
	if dataMapping != nil && len(dataMapping) != 0 {
		typeMap = dataMapping
	} else {
		typeMap = map[string]string{
			"TINYINT":    "SMALLINT",
			"MEDIUMINT":  "INTEGER",
			"INT":        "INTEGER",
			"YEAR":       "INTEGER",
			"INTEGER":    "INTEGER",
			"DATETIME":   "TIMESTAMP",
			"DATE":       "TIMESTAMP",
			"LONGTEXT":   "TEXT",
			"MEDIUMTEXT": "TEXT",
			"TINYTEXT":   "TEXT",
			// "DOUBLE":    "DOUBLE ",
			"NATIONAL VARCHAR": "VARCHAR",
			"FLOAT":            "REAL",
			// 二进制与BLOB类型
			"BIT":        "BYTEA",
			"BLOB":       "BYTEA",
			"TINYBLOB":   "BYTEA",
			"MEDIUMBLOB": "BYTEA",
			"LONGBLOB":   "BYTEA",
			"BINARY":     "BYTEA",
			"VARBINARY":  "BYTEA",
			"ENUM":       "VARCHAR",
		}
	}

	// 处理 UNSIGNED 映射（按更大范围类型映射，优先于一般类型映射）
	sql = regexp.MustCompile(`(?i)\bTINYINT\s+UNSIGNED\b`).ReplaceAllString(sql, "SMALLINT")
	sql = regexp.MustCompile(`(?i)\bSMALLINT\s+UNSIGNED\b`).ReplaceAllString(sql, "INTEGER")
	sql = regexp.MustCompile(`(?i)\b(INT|INTEGER)\s+UNSIGNED\b`).ReplaceAllString(sql, "BIGINT")
	// BIGINT UNSIGNED 超过 BIGINT 范围，映射为 NUMERIC(20)
	sql = regexp.MustCompile(`(?i)\bBIGINT\s+UNSIGNED\b`).ReplaceAllString(sql, "NUMERIC(20)")
	// 移除残留的 UNSIGNED 关键字
	sql = regexp.MustCompile(`(?i)\bUNSIGNED\b`).ReplaceAllString(sql, "")

	// 一般类型映射
	for mysqlType, kwdbType := range typeMap {
		pattern := fmt.Sprintf(`(?i)\b%s\b`, mysqlType)
		sql = regexp.MustCompile(pattern).ReplaceAllString(sql, kwdbType)
	}
	// 仅当作为数据类型使用时转换 SET(...) -> VARCHAR(255)，避免误伤 CHARACTER SET
	sql = regexp.MustCompile(`(?i)\bSET\s*\([^)]*\)`).ReplaceAllString(sql, "VARCHAR(255)")

	// 处理ENUM类型的特殊情况
	enumPattern := regexp.MustCompile(`(?i)\bVARCHAR\s*\([^)]*\)`)
	if enumPattern.MatchString(sql) {
		// 如果原来是ENUM，转换为VARCHAR(255)
		sql = regexp.MustCompile(`(?i)\bVARCHAR\s*\([^)]*\)`).ReplaceAllStringFunc(sql, func(match string) string {
			if strings.Contains(strings.ToUpper(match), "'") {
				return "VARCHAR(255)"
			}
			return match
		})
	}

	return sql
}

// convertEnumColumnsToCheck 将列定义中的 ENUM(...) 转换为 VARCHAR(255) 并添加 CHECK 约束
// 示例：status ENUM('active','inactive') -> status VARCHAR(255) CHECK (status IN ('active','inactive'))
func (c *MySQLToKWDBConverter) convertEnumColumnsToCheck(sql string) string {
	// 仅在 CREATE TABLE 的列定义中进行替换，简单正则足以应对常见情况
	// 捕获：列名 + ENUM(值列表)
	re := regexp.MustCompile(`(?i)(\b[A-Za-z_][A-Za-z0-9_]*\b)\s+ENUM\s*\(([^)]*)\)`) // 不包含反引号，已在前面移除
	return re.ReplaceAllStringFunc(sql, func(m string) string {
		sub := re.FindStringSubmatch(m)
		if len(sub) < 3 {
			return m
		}
		col := sub[1]
		list := strings.TrimSpace(sub[2])
		// 保留后续的约束（NOT NULL/DEFAULT等），因为我们只替换 ENUM(...) 部分
		// 生成：<col> VARCHAR(255) CHECK (<col> IN (<list>))
		return fmt.Sprintf("%s VARCHAR(255) CHECK (%s IN (%s))", col, col, list)
	})
}

// convertAutoIncrement 转换AUTO_INCREMENT
func (c *MySQLToKWDBConverter) convertAutoIncrement(sql string) string {
	// 针对列定义包含 AUTO_INCREMENT 的直接映射
	// BIGINT AUTO_INCREMENT -> BIGSERIAL（保留 PRIMARY KEY 如存在）
	sql = regexp.MustCompile(`(?i)(\b\w+\b)\s+BIGINT\s+AUTO_INCREMENT(\s+PRIMARY\s+KEY)?`).ReplaceAllString(sql, "$1 BIGSERIAL$2")
	// INT/INTEGER AUTO_INCREMENT -> SERIAL
	sql = regexp.MustCompile(`(?i)(\b\w+\b)\s+(INT|INTEGER)\s+AUTO_INCREMENT(\s+PRIMARY\s+KEY)?`).ReplaceAllString(sql, "$1 SERIAL$3")

	// 对未显式包含 AUTO_INCREMENT 但为主键的常见写法做容错转换
	// id INT PRIMARY KEY -> id SERIAL PRIMARY KEY（当没有默认值时）
	sql = regexp.MustCompile(`(?i)(\b\w+\b)\s+(INT|INTEGER)\s+PRIMARY\s+KEY`).ReplaceAllString(sql, "$1 SERIAL PRIMARY KEY")
	sql = regexp.MustCompile(`(?i)(\b\w+\b)\s+(INT|INTEGER)\s+NOT\s+NULL\s+PRIMARY\s+KEY`).ReplaceAllString(sql, "$1 SERIAL PRIMARY KEY")

	// 清理残留的 AUTO_INCREMENT 或表级 AUTO_INCREMENT=xxx
	sql = regexp.MustCompile(`(?i)\s+AUTO_INCREMENT\s*=\s*\d+`).ReplaceAllString(sql, "")
	sql = regexp.MustCompile(`(?i)\s+AUTO_INCREMENT`).ReplaceAllString(sql, "")

	return sql
}

// removeMySQLOptions 移除MySQL特有的表选项
func (c *MySQLToKWDBConverter) removeMySQLOptions(sql string) string {
	// 移除ENGINE选项
	sql = regexp.MustCompile(`(?i)\s+ENGINE\s*=\s*\w+`).ReplaceAllString(sql, "")

	// 移除AUTO_INCREMENT选项
	sql = regexp.MustCompile(`(?i)\s+AUTO_INCREMENT\s*=\s*\d+`).ReplaceAllString(sql, "")

	// 移除ROW_FORMAT选项
	sql = regexp.MustCompile(`(?i)\s+ROW_FORMAT\s*=\s*\w+`).ReplaceAllString(sql, "")

	// 移除KEY_BLOCK_SIZE选项
	sql = regexp.MustCompile(`(?i)\s+KEY_BLOCK_SIZE\s*=\s*\d+`).ReplaceAllString(sql, "")

	return sql
}

// removeCharsetAndCollation 移除字符集和排序规则
func (c *MySQLToKWDBConverter) removeCharsetAndCollation(sql string) string {
	// 移除DEFAULT CHARSET
	sql = regexp.MustCompile(`(?i)\s+DEFAULT\s+CHARSET\s*=\s*\w+`).ReplaceAllString(sql, "")
	sql = regexp.MustCompile(`(?i)\s+DEFAULT\s+CHARSET\s+\w+`).ReplaceAllString(sql, "")

	// 移除CHARACTER SET
	sql = regexp.MustCompile(`(?i)\s+CHARACTER\s+SET\s+\w+`).ReplaceAllString(sql, "")
	sql = regexp.MustCompile(`(?i)\s+CHARACTER\s+SET\s*=\s*[\w\d_]+`).ReplaceAllString(sql, "")

	// 移除COLLATE（支持 COLLATE name 和 COLLATE=name 两种写法）
	sql = regexp.MustCompile(`(?i)\s+COLLATE\s+\w+`).ReplaceAllString(sql, "")
	sql = regexp.MustCompile(`(?i)\s+COLLATE\s*=\s*[\w\d_]+`).ReplaceAllString(sql, "")

	return sql
}

// ensureIfNotExists 在CREATE语句中插入 IF NOT EXISTS，使其具备幂等性
func (c *MySQLToKWDBConverter) ensureIfNotExists(obj string, sql string) string {
	switch strings.ToUpper(strings.TrimSpace(obj)) {
	case "TABLE":
		// 已包含则不处理
		if regexp.MustCompile(`(?i)^CREATE\s+TABLE\s+IF\s+NOT\s+EXISTS\b`).MatchString(sql) {
			return sql
		}
		re := regexp.MustCompile(`(?i)^(CREATE\s+TABLE\s+)`)
		return re.ReplaceAllString(sql, "CREATE TABLE IF NOT EXISTS ")
	case "VIEW":
		if regexp.MustCompile(`(?i)^CREATE\s+VIEW\s+IF\s+NOT\s+EXISTS\b`).MatchString(sql) {
			return sql
		}
		re := regexp.MustCompile(`(?i)^(CREATE\s+VIEW\s+)`)
		return re.ReplaceAllString(sql, "CREATE VIEW IF NOT EXISTS ")
	case "SEQUENCE":
		if regexp.MustCompile(`(?i)^CREATE\s+SEQUENCE\s+IF\s+NOT\s+EXISTS\b`).MatchString(sql) {
			return sql
		}
		re := regexp.MustCompile(`(?i)^(CREATE\s+SEQUENCE\s+)`)
		return re.ReplaceAllString(sql, "CREATE SEQUENCE IF NOT EXISTS ")
	case "DATABASE":
		if regexp.MustCompile(`(?i)^CREATE\s+DATABASE\s+IF\s+NOT\s+EXISTS\b`).MatchString(sql) {
			return sql
		}
		re := regexp.MustCompile(`(?i)^(CREATE\s+DATABASE\s+)`)
		return re.ReplaceAllString(sql, "CREATE DATABASE IF NOT EXISTS ")
	default:
		return sql
	}
}

// removeMySQLIdentifierQuotes 移除MySQL反引号，仅在不处于字符串文本时处理
func (c *MySQLToKWDBConverter) removeMySQLIdentifierQuotes(sql string) string {
	// 为避免破坏字符串字面量，仅在非字符串上下文移除反引号
	var b strings.Builder
	b.Grow(len(sql))
	inSingle := false // '...'
	inDouble := false // "..."

	// 使用基于字节的扫描，并处理 '' 作为单引号转义的情况
	for i := 0; i < len(sql); i++ {
		ch := sql[i]
		if ch == '\'' && !inDouble {
			b.WriteByte(ch)
			if inSingle {
				// 处理 '' 转义，保持在字符串内
				if i+1 < len(sql) && sql[i+1] == '\'' {
					i++
					b.WriteByte('\'')
				} else {
					inSingle = false
				}
			} else {
				inSingle = true
			}
			continue
		}
		if ch == '"' && !inSingle {
			b.WriteByte(ch)
			// 切换双引号上下文（尽管SQL中双引号常用于标识符）
			inDouble = !inDouble
			continue
		}
		if ch == '`' && !inSingle && !inDouble {
			// 丢弃反引号
			continue
		}
		b.WriteByte(ch)
	}
	return b.String()
}

// stripIntegerDisplayWidth 去除整数类型的显示宽度，如 INT(11)、BIGINT(20)、SMALLINT(1)
func (c *MySQLToKWDBConverter) stripIntegerDisplayWidth(sql string) string {
	re := regexp.MustCompile(`(?i)\b(TINYINT|SMALLINT|MEDIUMINT|INT|INTEGER|BIGINT)\s*\(\s*\d+\s*\)`)
	return re.ReplaceAllString(sql, "$1")
}

// removeColumnComments 移除列定义中的 COMMENT '...'
func (c *MySQLToKWDBConverter) removeColumnComments(sql string) string {
	re := regexp.MustCompile(`(?i)\s+COMMENT\s+'(?:''|[^'])*'`)
	return re.ReplaceAllString(sql, "")
}

// removeTableComment 移除表级 COMMENT='...'
func (c *MySQLToKWDBConverter) removeTableComment(sql string) string {
	reEq := regexp.MustCompile(`(?i)\)\s*COMMENT\s*=\s*'(?:''|[^'])*'`)
	sql = reEq.ReplaceAllString(sql, ")")
	reNoEq := regexp.MustCompile(`(?i)\)\s*COMMENT\s*'(?:''|[^'])*'`)
	sql = reNoEq.ReplaceAllString(sql, ")")
	return sql
}

// extractComments 提取列与表级注释，并从原始SQL中移除这些注释。
// 返回：移除注释后的SQL、表名、表注释、列注释映射（列名->注释文本）。
func (c *MySQLToKWDBConverter) extractComments(sql string) (string, string, string, map[string]string) {
	// 提取表名（在移除反引号后）
	tableName := ""
	reTbl := regexp.MustCompile(`(?i)CREATE\s+TABLE\s+(IF\s+NOT\s+EXISTS\s+)?([^\s(]+)`) // 捕获到'('前的表名
	if m := reTbl.FindStringSubmatch(sql); len(m) >= 3 {
		tableName = m[2]
	}

	// 提取列注释（逐行匹配行首列定义中的 COMMENT '...')
	columnComments := make(map[string]string)
	reCol := regexp.MustCompile(`(?im)^\s*("?[\w$]+"?)\s+[^,\n]*?\s+COMMENT\s+'((?:''|[^'])*)'`)
	for _, m := range reCol.FindAllStringSubmatch(sql, -1) {
		if len(m) >= 3 {
			col := m[1]
			comment := m[2]
			// 记录注释
			columnComments[col] = comment
		}
	}

	// 从SQL中移除列注释
	sql = c.removeColumnComments(sql)

	// 提取表级注释
	tableComment := ""
	reEq := regexp.MustCompile(`(?i)\)\s*COMMENT\s*=\s*'((?:''|[^'])*)'`)
	if m := reEq.FindStringSubmatch(sql); len(m) >= 2 {
		tableComment = m[1]
	} else {
		reNoEq := regexp.MustCompile(`(?i)\)\s*COMMENT\s*'((?:''|[^'])*)'`)
		if m2 := reNoEq.FindStringSubmatch(sql); len(m2) >= 2 {
			tableComment = m2[1]
		}
	}

	// 从SQL中移除表注释
	sql = c.removeTableComment(sql)

	return sql, tableName, tableComment, columnComments
}

// transformInlineKeys 转换或移除表内 KEY 定义：
// - UNIQUE KEY name (cols) -> UNIQUE (cols)
// - KEY/INDEX name (cols) -> 移除
func (c *MySQLToKWDBConverter) transformInlineKeys(sql string) string {
	reUniqueKey := regexp.MustCompile(`(?i),\s*UNIQUE\s+KEY\s+\w+\s*\(([^)]*)\)`)
	sql = reUniqueKey.ReplaceAllString(sql, ", UNIQUE ($1)")

	reUniqueIndex := regexp.MustCompile(`(?i),\s*UNIQUE\s+INDEX\s+\w+\s*\(([^)]*)\)`)
	sql = reUniqueIndex.ReplaceAllString(sql, ", UNIQUE ($1)")

	reKey := regexp.MustCompile(`(?i),\s*(FULLTEXT\s+|SPATIAL\s+)?KEY\s+\w+\s*\(([^)]*)\)`)
	sql = reKey.ReplaceAllString(sql, "")

	reIndex := regexp.MustCompile(`(?i),\s*INDEX\s+\w+\s*\(([^)]*)\)`)
	sql = reIndex.ReplaceAllString(sql, "")

	return sql
}

// quoteReservedIdentifiers 为可能与SQL关键字冲突的标识符加双引号
// 仅在CREATE TABLE的列定义位置生效，避免影响其他上下文。
func (c *MySQLToKWDBConverter) quoteReservedIdentifiers(sql string) string {
	// 常见可能冲突的列名集合（小写比较）
	reserved := []string{"index", "type", "user", "order", "group", "sql"}
	for _, kw := range reserved {
		// 在列定义起始位置匹配：在'('或','之后跟随关键字和空格
		// 使用大小写不敏感匹配，并仅替换列定义开头的标识符
		re := regexp.MustCompile(`(?i)([,(]\s*)` + kw + `\s+`)
		sql = re.ReplaceAllString(sql, `$1"`+kw+`" `)
	}
	return sql
}

// GetConversionWarnings 获取转换警告
func (c *MySQLToKWDBConverter) GetConversionWarnings(stmt parser.SQLStatement) []string {
	var warnings []string

	switch stmt.Type {
	case parser.CreateProcedure:
		warnings = append(warnings, "存储过程可能不被KWDB完全支持")
	case parser.CreateFunction:
		warnings = append(warnings, "自定义函数可能不被KWDB完全支持")
	case parser.CreateTrigger:
		warnings = append(warnings, "触发器可能不被KWDB完全支持")
	}

	// 检查是否包含不支持的特性
	sql := strings.ToUpper(stmt.Content)
	if strings.Contains(sql, "ENUM") && !strings.Contains(sql, "UNKNOWN") {
		warnings = append(warnings, "ENUM类型已转换为VARCHAR，可能需要手动调整")
	}
	if strings.Contains(sql, "SET") && !strings.Contains(sql, "UNKNOWN") {
		warnings = append(warnings, "SET类型已转换为VARCHAR，可能需要手动调整")
	}
	if strings.Contains(sql, "AUTO_INCREMENT") && !strings.Contains(sql, "UNKNOWN") {
		warnings = append(warnings, "AUTO_INCREMENT已转换为SERIAL，请检查序列设置")
	}

	return warnings
}

// convertDropTable 转换DROP TABLE语句，添加CASCADE以处理外键依赖
func (c *MySQLToKWDBConverter) convertDropTable(stmt parser.SQLStatement) string {
	sql := c.removeMySQLIdentifierQuotes(stmt.Content)
	// 若已包含 CASCADE 则不重复添加
	upper := strings.ToUpper(sql)
	if strings.Contains(upper, " CASCADE;") || strings.Contains(upper, " CASCADE ") {
		return sql
	}
	// 在语句末尾分号前插入 CASCADE
	re := regexp.MustCompile(`(?i);\s*$`)
	return re.ReplaceAllString(sql, " CASCADE;")
}

// convertBinaryLiterals 将MySQL的二进制字面量转换为PG/KWDB可接受的bytea表达
// - _binary '...'  => E'...'::bytea (escape风格)
// - X'ABCD'        => E'\\xABCD'::bytea (bytea十六进制输入)
func (c *MySQLToKWDBConverter) convertBinaryLiterals(sql string) string {
	// 处理 _binary '...'
	reBinary := regexp.MustCompile(`(?i)_binary\s*'((?:''|[^'])*)'`)
	sql = reBinary.ReplaceAllStringFunc(sql, func(m string) string {
		// 提取内容
		sub := reBinary.FindStringSubmatch(m)
		if len(sub) == 2 {
			content := sub[1]
			// 规范MySQL的反斜杠转义为PG的escape格式（\000 等）
			normalized := normalizeMySQLBinaryEscapes(content)
			// 进一步将非可打印/高位字节转为八进制，以避免编码问题
			normalized = escapeNonPrintableToOctal(normalized)
			// 直接转换为 E'...'::bytea（PG 支持的 escape 风格输入）
			return "E'" + normalized + "'::bytea"
		}
		return m
	})

	// 处理十六进制字面量 X'ABCD'
	reHex := regexp.MustCompile(`(?i)\bX'([0-9A-Fa-f]+)'`)
	sql = reHex.ReplaceAllString(sql, "E'\\\\x$1'::bytea")

	return sql
}

// normalizeMySQLBinaryEscapes 将MySQL二进制字符串中的转义序列转换为PG可识别的escape格式
func normalizeMySQLBinaryEscapes(s string) string {
	// 常见控制字符：按PG的八进制转义（三位）
	replacers := []struct{ from, to string }{
		{"\\0", "\\000"}, // NUL
		{"\\Z", "\\032"}, // Ctrl-Z
		{"\\b", "\\010"}, // Backspace
		{"\\t", "\\011"}, // Tab
		{"\\n", "\\012"}, // Newline
		{"\\r", "\\015"}, // Carriage Return
		{"\\a", "\\007"}, // Bell
	}
	for _, r := range replacers {
		s = strings.ReplaceAll(s, r.from, r.to)
	}

	// 将 \xHH 转为对应的八进制形式 \\OOO
	reHexByte := regexp.MustCompile(`(?i)\\x([0-9A-F]{2})`)
	s = reHexByte.ReplaceAllStringFunc(s, func(m string) string {
		sub := reHexByte.FindStringSubmatch(m)
		if len(sub) == 2 {
			// 解析十六进制字节并格式化为三位八进制
			val, err := strconv.ParseInt(sub[1], 16, 64)
			if err == nil {
				return fmt.Sprintf("\\%03o", val)
			}
		}
		return m
	})

	// 保持 \\, \' 等按原样，E''会正确处理
	return s
}

// 将非可打印ASCII字符转义为PG可识别的八进制序列（\\OOO）
func escapeNonPrintableToOctal(s string) string {
	var b strings.Builder
	// 逐字节处理，避免UTF-8合并为多字节后丢失原始值
	bs := []byte(s)
	for i := 0; i < len(bs); i++ {
		ch := bs[i]
		// 保留已存在的八进制转义：\\NNN
		if ch == '\\' && i+3 < len(bs) {
			if bs[i+1] >= '0' && bs[i+1] <= '7' &&
				bs[i+2] >= '0' && bs[i+2] <= '7' &&
				bs[i+3] >= '0' && bs[i+3] <= '7' {
				b.WriteByte('\\')
				b.WriteByte(bs[i+1])
				b.WriteByte(bs[i+2])
				b.WriteByte(bs[i+3])
				i += 3
				continue
			}
		}
		// 其他情况：将不可打印或高位字节转为八进制
		if ch < 32 || ch > 126 {
			b.WriteString(fmt.Sprintf("\\%03o", ch))
		} else {
			b.WriteByte(ch)
		}
	}
	return b.String()
}
